
#include "SimpleIndexingCache.h"

SimpleIndexingCache::SimpleIndexingCache(){
	timer = new Timer();
}

SimpleIndexingCache::~SimpleIndexingCache(){

	for (boost::unordered_map<key_t, PubPkt*>::iterator it = cacheTable.begin(); it != cacheTable.end(); it++){
		//remove(make_pair(it->second->getTimeStamp().getTimeVal(), it->first));
		delete it->second;
	}
	for(boost::unordered_map<int, boost::unordered_map<std::string, SimpleIndexType*> >::iterator it2 = indexMap.begin(); it2!= indexMap.end(); it2++){
		for(boost::unordered_map<std::string, SimpleIndexType*>::iterator it3 = it2->second.begin(); it3!= it2->second.end(); it3++){
			if (it3->second->valType == INT){
				delete it3->second->intIndex;
			}
			if (it3->second->valType == FLOAT){
				delete it3->second->floatIndex;
			}
			if (it3->second->valType == BOOL){
				delete it3->second->boolIndex;
			}
			if (it3->second->valType == STRING){
				delete it3->second->stringIndex;
			}
			free (it3->second);
		}
	}

}

bool SimpleIndexingCache::insert(PubPkt *pkt, key_t k){
	timer->start();

	boost::unordered_map<int, boost::unordered_map<std::string, SimpleIndexType*> >::iterator it = indexMap.find(pkt->getEventType());
	if (it != indexMap.end()){
	//	cout << "Tipo già presente nell'indice" << endl;
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			boost::unordered_map<std::string, SimpleIndexType*>::iterator it2 = it->second.find(attribute.name);
			if (it2 != it->second.end()){
				SimpleIndexType* index = it2->second;
				if (index->valType==INT){
					std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->intIndex->find(attribute.intVal);
					if (it3 != index->intIndex->end()){
						it3->second.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
					} else {
						std::set<std::pair<timestamp_t, key_t> > keySet;
						keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
						index->intIndex->insert(make_pair(attribute.intVal, keySet));
					}
					continue;
				}
				if (index->valType == FLOAT){
					std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->floatIndex->find(attribute.floatVal);
					if (it3 != index->floatIndex->end()){
						it3->second.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
					} else {
						std::set<std::pair<timestamp_t, key_t> > keySet;
						keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
						index->floatIndex->insert(make_pair(attribute.floatVal, keySet));
					}
					continue;
				}
				if (index->valType == BOOL){
					boost::unordered_map<bool, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->boolIndex->find(attribute.boolVal);
					if (it3 != index->boolIndex->end()){
						it3->second.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
					} else {
						std::set<std::pair<timestamp_t, key_t> > keySet;
						keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
						index->boolIndex->insert(make_pair(attribute.boolVal, keySet));
					}
					continue;
				}
				if (index->valType == STRING){
					boost::unordered_map<std::string, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->stringIndex->find(attribute.stringVal);
					if (it3 != index->stringIndex->end()){
						it3->second.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
					} else {
						std::set<std::pair<timestamp_t, key_t> > keySet;
						keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
						index->stringIndex->insert(make_pair(attribute.stringVal, keySet));
					}
					continue;
				}
			}
		}
	} else {
		// cout << "Tipo non trovato nell'indice" << endl;
		boost::unordered_map<std::string, SimpleIndexType*> attributeMap;
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			SimpleIndexType* indexType = (SimpleIndexType*)malloc(sizeof(SimpleIndexType));
			indexType->valType = attribute.type;
			if (indexType->valType==INT) {
				indexType->intIndex = new std::map<int, std::set<std::pair<timestamp_t, key_t> > >();
				std::set<std::pair<timestamp_t, key_t> > keySet;
				keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
				indexType->intIndex->insert(make_pair(attribute.intVal, keySet));
				attributeMap.insert(make_pair(attribute.name, indexType));
				continue;
			}
			if (indexType->valType==FLOAT) {
				indexType->floatIndex = new std::map<float, std::set<std::pair<timestamp_t, key_t> > >();
				std::set<std::pair<timestamp_t, key_t> > keySet;
				keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
				indexType->floatIndex->insert(make_pair(attribute.floatVal, keySet));
				attributeMap.insert(make_pair(attribute.name, indexType));
				continue;
			}
			if (indexType->valType==BOOL){
				indexType->boolIndex = new boost::unordered_map<bool, std::set<std::pair<timestamp_t, key_t> > >();
				std::set<std::pair<timestamp_t, key_t> > keySet;
				keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
				indexType->boolIndex->insert(make_pair(attribute.boolVal, keySet));
				attributeMap.insert(make_pair(attribute.name, indexType));
				continue;
			}
			if (indexType->valType==STRING){
				indexType->stringIndex = new boost::unordered_map<std::string, std::set<std::pair<timestamp_t, key_t> > >();
				std::set<std::pair<timestamp_t, key_t> > keySet;
				keySet.insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
				indexType->stringIndex->insert(make_pair(attribute.stringVal, keySet));
				attributeMap.insert(make_pair(attribute.name, indexType));
				continue;
			}
		}
		indexMap.insert(make_pair(pkt->getEventType(), attributeMap));
	}
	cout << "Memory: CacheSimple: insertion into index completed." << endl;

	cacheTable.insert(make_pair(k, pkt));
	cout << "Memory: CacheSimple: insertion into cache completed." << endl;
	// cout << "Inserimento in cache effettuato" << endl;
	// cout << "Dimensione cache: " << cacheTable.size() << endl;

	timer->stop();
	cout << "Memory: CacheSimple: insertion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
	// cout << ">>> Insertion time: ";
	// cout << timer->getElapsedTimeInMilliSec();
	// cout << " milliseconds" << endl;
	return true;
}

bool SimpleIndexingCache::remove(std::pair<timestamp_t, key_t> key){
	timer->start();

	// cout << "Inizio rimozione..." << endl;
	boost::unordered_map<key_t, PubPkt*>::iterator it = cacheTable.find(key.second);
	if (it != cacheTable.end()){
		// cout << "Chiave trovata" << endl;
		PubPkt* pkt = it->second;
		boost::unordered_map<int, boost::unordered_map<std::string, SimpleIndexType*> >::iterator it = indexMap.find(pkt->getEventType());
		if (it != indexMap.end()){
			for (int i=0; i<pkt->getAttributesNum(); i++){
				Attribute attribute = pkt->getAttribute(i);
				boost::unordered_map<std::string, SimpleIndexType*>::iterator it2 = it->second.find(attribute.name);
				if (it2 != it->second.end()){
					SimpleIndexType* index = it2->second;
					if (index->valType==INT){
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->intIndex->find(attribute.intVal);
						if (it3 != index->intIndex->end()){
							it3->second.erase(key);
						}
						continue;
					}
					if (index->valType == FLOAT){
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->floatIndex->find(attribute.floatVal);
						if (it3 != index->floatIndex->end()){
							it3->second.erase(key);
						}
						continue;
					}
					if (index->valType == BOOL){
						boost::unordered_map<bool, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->boolIndex->find(attribute.boolVal);
						if (it3 != index->boolIndex->end()){
							it3->second.erase(key);
						}
						continue;
					}
					if (index->valType == STRING){
						boost::unordered_map<std::string, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->stringIndex->find(attribute.stringVal);
						if (it3 != index->stringIndex->end()){
							it3->second.erase(key);
						}
						continue;
					}
				}
			}
		}
		cout << "Memory: CacheSimple: remotion from index completed." << endl;

		cacheTable.erase(key.second);
		if (pkt->decRefCount()) delete pkt;
		cout << "Memory: CacheSimple: remotion from cache completed." << endl;
		// cout << "Rimozione effettuata" << endl;
		// cout << "...fine rimozione" << endl;

		timer->stop();
		// cout << ">>> Remotion time: ";
		// cout << timer->getElapsedTimeInMilliSec();
		// cout << " milliseconds" << endl;
		cout << "Memory: CacheSimple: remotion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
		return true;
	}
	// cout << "...fine rimozione" << endl;
	cout << "Memory: CacheSimple: remotion failed: message not found." << endl;
	return false;

}

void SimpleIndexingCache::extract(int type, Constraint* parCostraint, int parNumConstraint, timestamp_t parEntryTimestamp, float window, OccurrenceType occurrence, std::set<PubPkt*> &resultSet){
	timer->start();

	cout << "Memory: CacheSimple: start index exploration..." << endl;
	// cout << "Inizio estrazione..." << endl;
//	std::set<PubPkt*> resultSet;

	boost::unordered_map<int, boost::unordered_map<std::string, SimpleIndexType*> >::iterator it = indexMap.find(type);
	if (it != indexMap.end()){
		std::set<key_t> globalkeySet;
		bool firstConstraint = true;
		for (int i=0; i<parNumConstraint; i++){
			boost::unordered_map<std::string, SimpleIndexType*>::iterator it2 = it->second.find(parCostraint[i].name);
			if (it2 != it->second.end()){
				SimpleIndexType* index = it2->second;

				if (index->valType==INT){
					if (parCostraint[i].op == EQ) {
						std::set<key_t> localResultkeySet;
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->intIndex->find(parCostraint[i].intVal);
						if (it3 != index->intIndex->end()){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}
						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
					if (parCostraint[i].op == GT) {

						std::set<key_t> localResultkeySet;
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator intIt;
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator exactIt = index->intIndex->find(parCostraint[i].intVal);
						if (exactIt != index->intIndex->end()){
							intIt = exactIt;
						} else {
							intIt = index->intIndex->begin();
							while (intIt->first < parCostraint[i].intVal && intIt != index->intIndex->end()) intIt++;
						}
						for (std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = intIt; it3 != index->intIndex->end(); it3++){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}

						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
					if (parCostraint[i].op == LT) {
						std::set<key_t> localResultkeySet;
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator intIt;
						std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator exactIt = index->intIndex->find(parCostraint[i].intVal);
						if (exactIt != index->intIndex->end()){
							intIt = exactIt;
						} else {
							intIt = index->intIndex->end(); intIt--;
							while (intIt->first > parCostraint[i].intVal && intIt != index->intIndex->begin()) intIt--;
						}
						if (intIt != index->intIndex->begin()) intIt++;
						for (std::map<int, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->intIndex->begin(); it3 != intIt; it3++){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}

						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
				}
				if (index->valType == FLOAT){
					if (parCostraint[i].op == EQ) {
						std::set<key_t> localResultkeySet;
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->floatIndex->find(parCostraint[i].floatVal);
						if (it3 != index->floatIndex->end()){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}
						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
					if (parCostraint[i].op == GT) {
						std::set<key_t> localResultkeySet;
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator floatIt;
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator exactIt = index->floatIndex->find(parCostraint[i].floatVal);
						if (exactIt != index->floatIndex->end()){
							floatIt = exactIt;
						} else {
							floatIt = index->floatIndex->begin();
							while (floatIt->first < parCostraint[i].floatVal && floatIt != index->floatIndex->end()) floatIt++;
						}
						for (std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = floatIt; it3 != index->floatIndex->end(); it3++){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}

						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
					if (parCostraint[i].op == LT) {
						std::set<key_t> localResultkeySet;
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator floatIt;
						std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator exactIt = index->floatIndex->find(parCostraint[i].floatVal);
						if (exactIt != index->floatIndex->end()){
							floatIt = exactIt;
						} else {
							floatIt = index->floatIndex->end(); floatIt--;
							while (floatIt->first > parCostraint[i].floatVal) floatIt--;
						}
						floatIt++;
						for (std::map<float, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->floatIndex->begin(); it3 != floatIt; it3++){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}

						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
				}
				if (index->valType == BOOL){
					if (parCostraint[i].op == EQ) {
						std::set<key_t> localResultkeySet;
						boost::unordered_map<bool, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->boolIndex->find(parCostraint[i].boolVal);
						if (it3 != index->boolIndex->end()){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}
						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
				}
				if (index->valType == STRING){
					if (parCostraint[i].op == EQ) {
						std::set<key_t> localResultkeySet;
						boost::unordered_map<std::string, std::set<std::pair<timestamp_t, key_t> > >::iterator it3 = index->stringIndex->find(parCostraint[i].stringVal);
						if (it3 != index->stringIndex->end()){
							for (std::set<std::pair<timestamp_t, key_t> >::iterator it4 = it3->second.begin(); it4 != it3->second.end(); it4++){
								if ((it4->first <= parEntryTimestamp) && (it4->first >= (parEntryTimestamp - (int long)window))){
									if (firstConstraint){
										localResultkeySet.insert(it4->second);
									} else {
										std::set<key_t>::iterator it5 = globalkeySet.find(it4->second);
										if (it5 != globalkeySet.end()){
											localResultkeySet.insert(it4->second);
										}
									}
								}
							}
						}
						if (localResultkeySet.empty()){
							globalkeySet = localResultkeySet;
							break;
						} else {
							globalkeySet = localResultkeySet;
							firstConstraint = false;
							continue;
						}
					}
				}
			}
		}
		//FINE FOR
		if (occurrence == LAST) {
			std::set<key_t>::reverse_iterator itEnd = globalkeySet.rbegin();
			if (itEnd != globalkeySet.rend()){
				boost::unordered_map<key_t, PubPkt*>::iterator itCache = cacheTable.find(*itEnd);
				if (itCache != cacheTable.end()){
					resultSet.insert(itCache->second);
				}
			}
		} else {
			for (std::set<key_t>::iterator itEnd = globalkeySet.begin(); itEnd != globalkeySet.end(); itEnd++){
				boost::unordered_map<key_t, PubPkt*>::iterator itCache = cacheTable.find(*itEnd);
				if (itCache != cacheTable.end()){
					resultSet.insert(itCache->second);
				}
			}
		}
	}

	// cout << "... fine estrazione" << endl;

	timer->stop();
	cout << "Memory: CacheSimple: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
	// cout << ">>> Extraction time: ";
	// cout << timer->getElapsedTimeInMilliSec();
	// cout << " milliseconds" << endl;
//	return resultSet;
}

























